import { SearchOutlined } from '@ant-design/icons'
import { keywordCompare } from '@gnlab/utils'
import { useAsyncEffect, useRefValue } from '@gnlab/utils/env-react'
import { message, Tag, Button, Spin, Input } from 'antd'
import { EditModal, Empty } from '@/desktop/components'
import { useStreams } from '@/lib/utils'
import services from '@/services'
import { groupExternalTagsWithCorp, groupExternalTags } from '@/services/contact-types'
import type { ExternalTag, ExternalItemTag, ExternalTagGroup } from '@/services/contact-types'
import './index.module.less'

import type { TagGroupWithQywx } from './AddTag'
import AddTagModal from './AddTag'

type TagGroupWithCorpInfo = ExternalTagGroup & {
  isPersonal?: boolean
  corpid?: string
  corpname?: string
}

interface Props {
  visible: boolean
  title?: string // 对话框标题
  corpIds?: string[] // 若指定，则只返回这些企业的企业标签。若指定了 wxid，此属性会被忽略

  // 企业微信 id
  // - 指定了则选项中只包含其所属企业的标签，且会包含他的个人标签
  //  （用户可在选择的同时创建新的个人标签，因此上级可能需要在对话框关闭后重新加载标签列表）
  // - 不指定，则选项中包含当前所有企业微信所属企业的标签，不会包含个人标签
  wxid?: string

  choosed?: string[] | null // 初始已选中的标签
  choosedSwitchable?: boolean // 初始选中的标签是否允许取消选中，默认为 true
  onSwitch?: (tag: ExternalItemTag, choosed: boolean) => void // 某个标签被选中 / 取消选中时会触发此回调。
  // 指定此回调代表上级会在每次更新时执行对应业务逻辑，而不是在 onFinish() 时一次性执行，因此界面上将不再显示“取消”按钮
  onFinish: (choosed?: ExternalItemTag[]) => void // 结束选择时触发此回调，没有传入 choosed 说明用户点了“取消“
}

export default function ChooseExternalTags(props: Props) {
  const { visible, title, corpIds, wxid, choosed, choosedSwitchable, onSwitch, onFinish } = props

  return <EditModal
    title={title ?? '选择标签'}
    width="600px"
    open={visible}
    onCancel={() => onFinish()}
  >
    {visible ? (
      <ModalContent
        corpIds={corpIds}
        wxid={wxid}
        choosed={choosed}
        choosedSwitchable={choosedSwitchable}
        onSwitch={onSwitch}
        onFinish={onFinish}
      />
    ) : null}
  </EditModal>
}

function ModalContent(
  props: Pick<
    Props,
    'corpIds' | 'wxid' | 'choosed' | 'choosedSwitchable' | 'onSwitch' | 'onFinish'
  >,
) {
  const {
    corpIds: propsCorpIds,
    wxid,
    choosed: initialChoosed = [],
    choosedSwitchable: initialChoosedSwitchable = true,
    onSwitch,
    onFinish,
  } = props
  const [corps] = useStreams(services.qywxs.corps)

  const getCorpIdForWxid = useCallback((wxid: string) => {
    const usableQywxs = services.qywxs.usables.value
    if (usableQywxs.has(wxid)) return usableQywxs.get(wxid)!.corpid

    // 如果 wxid 不在企业微信列表里，可能当前是转交的客户，尝试从转交列表里找
    const transfer = services.transfers.receiveList.value.find(
      item => item.transfer.ownerWxid === wxid,
    )?.transfer
    return transfer ? transfer.ownerCorpId : null
  }, [])

  const corpIds = useMemo(() => {
    if (wxid !== undefined) {
      const corpId = getCorpIdForWxid(wxid)
      return corpId === null ? [] : [corpId]
    } else {
      return propsCorpIds ?? null
    }
  }, [propsCorpIds, wxid])

  // 加载标签
  const [tags, setTags] = useState<ExternalTag[] | null>(null)
  useAsyncEffect(async () => {
    const res = await services.externals.tags.getAllTags(corpIds ?? undefined)
    if (res.success) setTags(res.data)
    else void message.error(`标签加载失败：${res.error}`)
  }, [wxid, corpIds])

  // 搜索标签
  const [search, setSearch] = useState('')
  const filteredTags = useMemo(() => {
    if (!search) return tags
    return tags?.filter(tag => tag.isGroup || keywordCompare(search, tag.name))
  }, [tags, search])

  // 整理标签数据
  const groups = useMemo<TagGroupWithCorpInfo[] | null>(() => {
    if (!filteredTags) return null

    if (wxid !== undefined) {
      const groups = groupExternalTags(filteredTags.filter(t => t.isCorp || t.wxid === wxid))
      return [...groups.corp, ...groups.personal.map(g => ({ ...g, isPersonal: true }))]
    }

    const result: TagGroupWithCorpInfo[] = []
    for (const [corpid, corpGroups] of groupExternalTagsWithCorp(filteredTags).entries()) {
      const corpname = corps.get(corpid) ?? ''
      result.push(
        ...corpGroups.corp.map(group => ({ ...group, corpid, corpname, isPersonal: false })),
      )
    }
    return result
  }, [wxid, filteredTags, corps])

  const [choosed, setChoosed] = useState(() => new Set(initialChoosed))
  const choosedRef = useRefValue(choosed)
  const initialChoosedRef = useRefValue(initialChoosed)
  const switchTag = useCallback(
    (tag: ExternalItemTag) => {
      const { id } = tag

      const tagInitialChoosed = initialChoosedRef.current?.includes(id) ?? false
      if (tagInitialChoosed && !initialChoosedSwitchable) {
        void message.warning('不支持移除已添加的客户标签')
        return
      }

      const next = new Set(choosedRef.current)
      const nextChoosed = !next.has(id)
      if (next.has(id)) next.delete(id)
      else next.add(id)
      setChoosed(next)
      if (onSwitch) onSwitch(tag, nextChoosed)
    },
    [initialChoosedSwitchable, choosedRef, onSwitch],
  )

  const submit = useCallback(() => {
    onFinish(
      [...choosed].map(id => tags?.find(t => t.id === id)).filter((t): t is ExternalItemTag => !!t),
    )
  }, [tags, choosed, onFinish])

  const [adding, setAdding] = useState<TagGroupWithQywx | null>(null) // 正在进行“添加标签”的“个人标签”分组的 id
  const startAdd = useCallback(
    (group: TagGroupWithCorpInfo) =>
      setAdding({ ...group, wxid: wxid!, corpId: getCorpIdForWxid(wxid!) ?? '' }),
    [wxid],
  )
  const finishAdd = useCallback(
    (tag?: ExternalItemTag) => {
      if (tag) {
        setTags([...tags!, tag])
        setChoosed(new Set([tag.id, ...choosed]))
      }
      setAdding(null)
    },
    [tags, choosed],
  )

  return <>
    <Spin spinning={!groups} tip="载入中..." size="large">
      <div styleName="tags">
        <div styleName="search">
          <Input
            prefix={<SearchOutlined />}
            placeholder="搜索标签"
            value={search}
            onChange={e => setSearch(e.target.value)}
          />
        </div>

        {(groups ?? []).map(group => <GroupItem
          key={`${group.id}-${group.corpid ?? ''}`}
          group={group}
          choosed={choosed}
          switchTag={switchTag}
          onAdd={() => startAdd(group)}
        />)}

        {groups && !groups.length ? <Empty description="没有可选的标签" styleName="empty" /> : null}
      </div>
    </Spin>

    <footer>
      {!onSwitch ? <Button onClick={() => onFinish()}>取消</Button> : null}
      <Button type="primary" onClick={submit}>
        确定
      </Button>
    </footer>

    <AddTagModal adding={adding} onFinish={finishAdd} />
  </>
}

function GroupItem(props: {
  group: TagGroupWithCorpInfo
  choosed: Set<string>
  switchTag: (tag: ExternalItemTag) => void
  onAdd: () => void // 触发“添加标签”
}) {
  const { group, choosed, switchTag, onAdd } = props

  if (group.isPersonal !== true && !group.tags.length) return null

  return <div styleName="group">
    <header>
      <span styleName="group-name">
        {group.name}
        {group.isPersonal === true ? (
          <a styleName="add" onClick={onAdd}>
            添加标签
          </a>
        ) : null}
      </span>
      <span styleName="corpname">{group.corpname ?? ''}</span>
    </header>
    <section>
      {group.tags.map(tag => <Tag
        key={tag.id}
        onClick={() => switchTag(tag)}
        {...(choosed.has(tag.id) ? { color: '#4798EF' } : {})}
      >
        {tag.name}
      </Tag>)}
    </section>
  </div>
}
